﻿using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;

namespace SL3DControls
{
    public class DepthCanvas : Canvas
    {
        /// <summary>
        /// Attached property to store Depth of the layer. The higher the value the
        /// closer the layer is to the user.
        /// </summary>
        public static readonly DependencyProperty ZDepthProperty = DependencyProperty.RegisterAttached(
            "ZDepth",
            typeof(double),
            typeof(DepthCanvas),
            new PropertyMetadata(0d)
            );

        /// <summary>
        /// Dependency property to change the speed of rotation per mousemovement.
        /// A low value causes a fast and bigger rotation than a higher value.
        /// use 100 for a small change, use 25 for a bigger rotation or use 5 for a
        /// more extreme rotation and to see the 3d effect more clearly
        /// </summary>
        public static readonly DependencyProperty SpeedProperty =
            DependencyProperty.Register("Speed", typeof(double), typeof(DepthCanvas), new PropertyMetadata(100d));

        private Point _oldPos = new Point(double.NaN, double.NaN);

        public DepthCanvas()
        {
            Background = new SolidColorBrush(Colors.Gray);
            MouseMove += DepthCanvas_MouseMove;
            //Loaded += DepthCanvas_Loaded;
            this.LayoutUpdated += new System.EventHandler(DepthCanvas_LayoutUpdated); 
        }

        void DepthCanvas_LayoutUpdated(object sender, System.EventArgs e)
        {
            foreach (Panel item in Children.OfType<Panel>())
            {
                var plane = item.Projection as PlaneProjection;
                if (plane == null)
                {
                    item.Projection = new PlaneProjection
                                          {
                                              LocalOffsetZ = (double) item.GetValue(ZDepthProperty),
                                              RotationY = 0,
                                              RotationX = 0
                                          };
                }

            }
        }

        public double Speed
        {
            get { return (double)GetValue(SpeedProperty); }
            set { SetValue(SpeedProperty, value); }
        }

        public static void SetZDepth(UIElement element, double value)
        {
            element.SetValue(ZDepthProperty, value);
        }

        public static double GetZDepth(UIElement element)
        {
            return (double)element.GetValue(ZDepthProperty);
        }

        private void DepthCanvas_Loaded(object sender, RoutedEventArgs e)
        {
            foreach (Panel item in Children.OfType<Panel>())
            {

                item.Projection = new PlaneProjection
                                      {
                                          LocalOffsetZ = (double)item.GetValue(ZDepthProperty),
                                          RotationY = 0,
                                          RotationX = 0
                                      };

            }
        }

        private void DepthCanvas_MouseMove(object sender, MouseEventArgs e)
        {
            Point newPos = e.GetPosition(this);

            var deltaPos = new Point(0, 0);

            if (!double.IsNaN(_oldPos.X) && !double.IsNaN(_oldPos.Y))
            {
                deltaPos.X = _oldPos.X - newPos.X;
                deltaPos.Y = _oldPos.Y - newPos.Y;
                foreach (Canvas item in Children.OfType<Canvas>())
                {
                    item.Width = ActualWidth;
                    item.Height = ActualHeight;
                    var plane = item.Projection as PlaneProjection;
                    if (plane != null)
                    {
                        plane.LocalOffsetZ = (double)item.GetValue(ZDepthProperty);
                        plane.RotationX = -(newPos.Y - ActualHeight / 2) / Speed;
                        plane.RotationY = (newPos.X - ActualWidth / 2) / Speed;
                    }
                    else
                    {
                        item.Projection = new PlaneProjection
                                              {
                                                  LocalOffsetZ = 0,
                                                  RotationY = 0,
                                                  RotationX = 0
                                              };
                    }
                }
            }
            _oldPos = newPos;
        }
    }
}